/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.sober.dialect;

import com.jspx.sober.TableModels;
import com.jspx.utils.DateUtil;
import com.jspx.utils.ObjectUtil;
import com.jspx.utils.StringUtil;

import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.sql.*;
import java.util.Date;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-1-10
 * Time: 11:16:41
 * Oracle 数据库 SQL 匹配
 */
public class OracleDialect extends Dialect {
    //private EnvironmentTemplate envTemplate = EnvFactory.getEnvironmentTemplate();
    //private String encode = envTemplate.getString(Environment.encode);

    public OracleDialect() {                                                                              //number(1)
        standard_SQL.put(SQL_CREATE_TABLE, "CREATE TABLE ${" + KEY_TABLE_NAME + "} \n(\n" +
                " <#list column=" + KEY_COLUMN_LIST + ">${column},\n</#list>" +
                " \nCONSTRAINT \"${" + KEY_TABLE_NAME + "}_key\" PRIMARY KEY  (${" + KEY_PRIMARY_KEY + "})\n)");
        standard_SQL.put(Boolean.class.getName(), "${" + COLUMN_NAME + "} number(1) default <#if where=!" + COLUMN_DEFAULT + " >0<#else>1</#else></#if>");
        standard_SQL.put(boolean.class.getName(), "${" + COLUMN_NAME + "} number(1) default <#if where=!" + COLUMN_DEFAULT + " >0<#else>1</#else></#if>");
        standard_SQL.put(String.class.getName(), "${" + COLUMN_NAME + "} <#if where=" + COLUMN_LENGTH + "&gt;255>long<#else>varchar2(${" + COLUMN_LENGTH + "})</#else></#if> <#if where=" + COLUMN_DEFAULT + ">default '${" + COLUMN_DEFAULT + "}'</#if>");
        standard_SQL.put(Integer.class.getName(), "${" + COLUMN_NAME + "} NUMBER(10) <#if where=!" + KEY_FIELD_SERIAL + " >default <#if where=!" + COLUMN_DEFAULT + " >0<#else>${" + COLUMN_DEFAULT + "}</#else></#if></#if>");
        standard_SQL.put("int", "${" + COLUMN_NAME + "} <#if where=" + KEY_FIELD_SERIAL + ">SERIAL<#else>NUMBER(10)</#else></#if> <#if where=!" + KEY_FIELD_SERIAL + " >default <#if where=!" + COLUMN_DEFAULT + " >0<#else>${" + COLUMN_DEFAULT + "}</#else></#if></#if>");

        standard_SQL.put(Long.class.getName(), "${" + COLUMN_NAME + "} <#if where=" + COLUMN_LENGTH + "&gt;16>NUMBER(${" + COLUMN_LENGTH + "})<#else>NUMBER(16)</#else></#if> default <#if where=!" + COLUMN_DEFAULT + ">0<#else>${" + COLUMN_DEFAULT + "}</#else></#if>");
        standard_SQL.put("long", "${" + COLUMN_NAME + "} <#if where=" + COLUMN_LENGTH + "&gt;16>NUMBER(${" + COLUMN_LENGTH + "})<#else>NUMBER(16)</#else></#if> default <#if where=!" + COLUMN_DEFAULT + ">0<#else>${" + COLUMN_DEFAULT + "}</#else></#if>");

        standard_SQL.put(Double.class.getName(), "${" + COLUMN_NAME + "} BINARY_DOUBLE default <#if where=!" + COLUMN_DEFAULT + ">0<#else>${" + COLUMN_DEFAULT + "}</#else></#if>");
        standard_SQL.put("double", "${" + COLUMN_NAME + "} BINARY_DOUBLE default <#if where=!" + COLUMN_DEFAULT + ">0<#else>${" + COLUMN_DEFAULT + "}</#else></#if>");

        standard_SQL.put(Float.class.getName(), "${" + COLUMN_NAME + "} BINARY_FLOAT default <#if where=!" + COLUMN_DEFAULT + ">0<#else>${" + COLUMN_DEFAULT + "}</#else></#if>");
        standard_SQL.put("float", "${" + COLUMN_NAME + "} BINARY_FLOAT default <#if where=!" + COLUMN_DEFAULT + ">0<#else>${" + COLUMN_DEFAULT + "}</#else></#if>");

        standard_SQL.put(Date.class.getName(), "${" + COLUMN_NAME + "} TIMESTAMP default SYSDATE");
        standard_SQL.put(byte[].class.getName(), "${" + COLUMN_NAME + "} blob");
        standard_SQL.put(InputStream.class.getName(), "${" + COLUMN_NAME + "} blob");
        standard_SQL.put(char.class.getName(), "${" + COLUMN_NAME + "} char(2) NOT NULL default ''");
        standard_SQL.put(SQL_DROP_TABLE, "DROP TABLE \"${" + KEY_TABLE_NAME + "}\"");
        standard_SQL.put(FUN_TABLE_EXISTS, "SELECT count(*) FROM tab WHERE tname=upper('${" + KEY_TABLE_NAME + "}')");
    }

    public String getLimitString(String sql, int begin, int end, TableModels soberTable) {
        boolean isForUpdate = false;
        if (sql.toLowerCase().endsWith(" for update")) {
            sql = sql.substring(0, sql.length() - 11);
            isForUpdate = true;
        }

        StringBuilder pagingSelect = new StringBuilder(sql.length() + 100);
        if (begin > 0) {
            pagingSelect.append("SELECT * from ( SELECT row_.*, rownum rownum_ FROM ( ");
        } else {
            pagingSelect.append("SELECT * FROM ( ");
        }
        pagingSelect.append(sql);
        if (begin > 0) {
            pagingSelect.append(" ) row_ ) WHERE rownum_ BETWEEN ").append((begin + 1)).append(" and ").append(end);
        } else {
            pagingSelect.append(" ) WHERE rownum <=").append(end);
        }

        if (isForUpdate) {
            pagingSelect.append(" FOR UPDATE");
        }
        return pagingSelect.toString();
    }

    public void setPreparedStatementValue(PreparedStatement pstmt, int parameterIndex, Object obj) throws Exception {
        if (obj instanceof String) {
            pstmt.setString(parameterIndex, (String) obj);
            return;
        }
        if (obj instanceof Boolean) {
            pstmt.setInt(parameterIndex, ObjectUtil.toInt(ObjectUtil.toBoolean(obj)));
            return;
        }
        if (obj instanceof Clob) {
            pstmt.setClob(parameterIndex, (Clob) obj);
            return;
        }
        if (obj instanceof java.sql.Time) {
            pstmt.setTime(parameterIndex, (java.sql.Time) obj);
            return;
        }
        if (obj instanceof java.sql.Date) {
            pstmt.setDate(parameterIndex, (java.sql.Date) obj);
            return;
        }
        if (obj instanceof java.util.Date) {
            java.util.Date d = (java.util.Date) obj;
            pstmt.setTimestamp(parameterIndex, new Timestamp(d.getTime()));
            return;
        }
        if (obj instanceof InputStream) {
            InputStream b = (InputStream) obj;
            pstmt.setBinaryStream(parameterIndex, b, b.available());
            return;
        }
        if (obj instanceof Blob) {
            pstmt.setBlob(parameterIndex, (Blob) obj);
            return;
        }
        if (obj instanceof Integer) {
            pstmt.setInt(parameterIndex, (Integer) obj);
            return;
        }
        if (obj instanceof Float) {
            pstmt.setFloat(parameterIndex, (Float) obj);
            return;
        }
        if (obj instanceof Double) {
            pstmt.setDouble(parameterIndex, (Double) obj);
            return;
        }
        if (obj instanceof Array) {
            pstmt.setArray(parameterIndex, (Array) obj);
            return;
        }
        if (obj instanceof Short) {
            pstmt.setShort(parameterIndex, (Short) obj);
            return;
        }
        if (obj instanceof Long) {
            pstmt.setLong(parameterIndex, (Long) obj);
            return;
        }
        if (obj instanceof Ref) {
            pstmt.setRef(parameterIndex, (Ref) obj);
            return;
        }

        pstmt.setObject(parameterIndex, obj);
    }


    /**
     * @param rs    返回对象
     * @param index 索引
     * @return 返回查询结果
     * @throws SQLException
     */
    public Object getResultSetValue(ResultSet rs, int index) throws SQLException {
        String typeName = rs.getMetaData().getColumnTypeName(index).toLowerCase();
        int colSize = rs.getMetaData().getColumnDisplaySize(index);
        ///////大数值
        if (typeName.equals("ROWID")) {
            return rs.getString(index);
        }

        //短断整型
        if ((typeName.equals("int") && colSize < 4) || typeName.equals("short") || typeName.equals("smallint") || typeName.equals("int2") || typeName.equals("tinyint") || (typeName.equals("fixed") && colSize < 4)) {
            return rs.getShort(index);
        }
        //////////整型
        if (typeName.equals("int") || typeName.equals("integer") || typeName.equals("int4") || (typeName.equals("number") && colSize < 8) || (typeName.equals("fixed") && colSize < 19)) {
            return rs.getInt(index);
        }

        ///////长整型
        if (typeName.equals("long") || typeName.equals("bigint") || typeName.equals("int8") || (typeName.equals("fixed") && colSize > 18)) {
            return rs.getLong(index);
        }

        ///////单精度
        if (typeName.equals("integer") || typeName.equals("money") || typeName.equals("float") || typeName.equals("real") || typeName.equals("binary_float")) {
            return rs.getFloat(index);
        }
        ///////大数值
        if (typeName.equals("decimal")) {
            return rs.getBigDecimal(index);
        }
        ///////双精度
        if (typeName.equals("double") || typeName.equals("double precision") || typeName.equals("binary_double")) {
            return rs.getDouble(index);
        }

        ///////日期
        if (typeName.equals("date")) {
            java.sql.Date t = rs.getDate(index);
            if (t == null) return DateUtil.empty;
            return new java.util.Date(t.getTime());
        }

        ///////日期时间
        if (typeName.contains("timestamp") || typeName.equals("datetime"))
        {
            Timestamp t = rs.getTimestamp(index);
            if (t == null) return DateUtil.empty;
            return new java.util.Date(t.getTime());
        }


        ////////////时间
        if (typeName.equals("time")) {
            return rs.getTime(index);
        }

        ///////短字符串
        if (typeName.equals("char") || typeName.equals("nvarchar") || typeName.equals("varchar") || typeName.equals("varchar2") || typeName.equals("tinyblob")) {
            return rs.getString(index);
        }

        ////////////大文本类型
        if (typeName.equals("CLOB") || typeName.equals("mediumtext") || typeName.equals(" long varchar")
                || typeName.equals("ntext") || typeName.equals("text") || typeName.equals("long raw")) {
            oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob(index);
            if (clob==null) return StringUtil.empty;
            Reader bodyReader = clob.getCharacterStream();

            StringWriter out = new StringWriter(255);
            try {
                char buf[] = new char[256];
                int i;
                while ((i = bodyReader.read(buf)) != -1) out.write(buf, 0, i);
                out.close();
                bodyReader.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return out.toString();
        }

        ///////二进制类型 文件类型
        if (typeName.equals("image") || typeName.equals("blob") || typeName.equals("mediumblob")
                || typeName.equals("longblob") || typeName.equals("dbclob")
                || typeName.equals("image") || typeName.equals("long byte")
                || typeName.equals("varbinary") || typeName.equals("binary") || typeName.equals("long byte")) {
            oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(index);
            return blob.getBinaryStream();
        }
        return rs.getObject(index);
    }

    public boolean supportsSequenceName()
    {
        return false;
    }


    public boolean supportsLimit() {
        return true;
    }

    public boolean commentPatch() {
        return false;
    }

}